package  
{
	//
	import by.blooddy.crypto.Base64;
	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.SecureSocket;
	import flash.net.Socket;
	import flash.utils.ByteArray;
	//
	/**
	 * Сервер готов к общению
	 * можно логиниться
	 * @eventType	flash.events.Event.INIT
	 */
	[Event(name = "init", type = "flash.events.Event")] 
	//
	/**
	 * Сервер вернул ошибку
	 * @eventType	flash.events.ErrorEvent.ERROR
	 */
	[Event(name = "error", type = "flash.events.ErrorEvent")] 
	//
	/**
	 * 
	 * @eventType	SMTPMail.LOGIN_ACCEPTED
	 */
	[Event(name = "loginAccepted", type = "flash.events.Event")] 
	//
	/**
	 * Письмо отправлено
	 * @eventType	SMTPMail.EMAIL_SENDED
	 */
	[Event(name = "eMailSended", type = "flash.events.Event")] 
	//
	/**
	 * Для работы требуется библиотека 
	 * http://www.blooddy.by/ru/crypto/
	 */
	//
	//
	//
	public class SMTPMail extends EventDispatcher
	{
		static public const LOGIN_ACCEPTED:String = "loginAccepted";
		static public const EMAIL_SENDED:String = "eMailSended";
		//
		static private const NEW_LINE:String = "'\r\n'";
		//
		private var _socket:Socket;
		private var _host:String;
		private var _state:uint=0;
		private var _ehloWait:Boolean=false;
		private var _userName:String;
		private var _pass:String;
		//
		private var _email:Array;
		
		public function SMTPMail(soket:Socket) 
		{
			_socket = soket;
			_socket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData);
		}
		
		public function connect(host:String,port:int=25):void 
		{
			_state = 0;
			_host = host;
			_socket.connect(host, port);
		}
		
		public function login(userName:String,pass:String):void 
		{
			if (!_socket || !_socket.connected || _state!=1) return;
			
			var bBytes:ByteArray = new ByteArray();
			bBytes.writeUTFBytes ( userName );
			_userName = Base64.encode ( bBytes );
			
			bBytes = new ByteArray();
			bBytes.writeUTFBytes ( pass );
			_pass = Base64.encode ( bBytes );
			
			write(["AUTH LOGIN"]);
		}
		
		public function sendMail ( from:String, dist:String, subject:String, mess:String, attachImg:ByteArray=null, fileName:String='' ) :void
		{
			if (!_socket || !_socket.connected || _state!=2) return;
			
			_email = [];
			_email.push (		"From: "	+	from,
								"To: "		+	dist,
								"Subject: "	+	subject,
								"Mime-Version: 1.0");
			if (attachImg != null){
				_email.push (	"Content-Type: multipart/mixed; boundary= boundaryboundary",
								"",
								"--boundaryboundary");
			}	
			_email.push (		"Content-Type: text/html; charset=UTF-8; format=flowed",
								"",
								mess );
			if (attachImg != null) {
				_email.push (	"--boundaryboundary",
								"Content-Type: application/octet-stream; name=\""+fileName+"\"", 
								"Content-Transfer-Encoding: base64",
								"Content-Disposition: attachment; filename=\""+fileName+"\"",
								"",
								Base64.encode ( attachImg ),
								"",
								"--boundaryboundary--\r\n");
			}
			_email.push (		".");
					
			write	([	"MAIL FROM: " 	+ from  ,
						"RCPT TO: " 	+ dist  ,
						"DATA"	]);
		}
		
		public function destroy():void 
		{
			if (!_socket) return;
			try 
			{
				write(["QUIT"]);
				_socket.close();
			}catch (err:Error)
			{
				
			}
			_socket.removeEventListener(ProgressEvent.SOCKET_DATA, onSocketData);
			_socket = null;
			_state = 0;
		}
		
		private function onSocketData(event:ProgressEvent):void 
		{
			var response:String = _socket.readUTFBytes ( _socket.bytesAvailable );
			
			var arResponse:Array = response.split(NEW_LINE);
			arResponse.pop();
			var arLine:Array;
			for each (var line:String in arResponse) 
			{
				arLine = line.split(' ');
				decode(arLine[0], arLine[1]);
			}
		}
		
		private function decode(code:String, val:String):void 
		{
			trace(code+' '+val)
			switch (code) 
			{
				case '211':	//	Ответ о состоянии системы или помощь
					
				break;
				case '214':	//	Сообщение-подсказка (помощь)
					
				break;
				case '220':	//	<имя_домена> служба готова к работе
					// сокет подключился и сервер сообщает что он готов к работе
					// некоторые сервера ругались если с ними здороваться по полному адресу подключения
					// а некоторые ругались если ничего в EHLO не передать
					var server:Array = _host.split('.');
					_ehloWait = true;
 					write(["EHLO "+server[server.length-2]+'.'+server[server.length-1]]);					
				break;
				case '235':	//	Authentication succeeded
					_state = 2;
					dispatchEvent(new Event(LOGIN_ACCEPTED));
				break;
				case '221':	//	<имя_домена> служба закрывает канал связи
					
				break;
				case '250':	//	Запрошенное действие почтовой транзакции успешно завершилось
					if (_ehloWait) {
						// поздоровались с сервером - он нам ответил что все хорошо и что он умеет - готов к работе
						_state = 1;
						dispatchEvent(new Event(Event.INIT));
						_ehloWait = false;
					}else if (_state==3) {
						_state = 2;
						dispatchEvent(new Event(EMAIL_SENDED));
					}
				break;
				case '251':	//	Данный адресат не является местным; сообщение будет передано по маршруту <forward-path>
					
				break;
				case '334':	//	Сервер запрашивает имя или пароль
					if (val == 'VXNlcm5hbWU6'){
						write([_userName]);
					}else if (val == 'UGFzc3dvcmQ6'){
						write([_pass]);
					}
				break;
				case '354':	//	Начинай передачу сообщения. Сообщение заканчивается комбинацией CRLF-точка-CRLF
					_state = 3;
					write(_email);
				break;
				case '421':	//	<имя_домена> служба недоступна; соединение закрывается
				//break;
				case '450':	//	Запрошенная команда почтовой транзакции не выполнена, так как почтовый ящик недоступен
				//break;
				case '451':	//	Запрошенная команда не выполнена; произошла локальная ошибка при обработке сообщения
				//break;
				case '451':	//	Запрошенная команда не выполнена; системе не хватило ресурсов
				//break;
				case '500':	//	Синтаксическая ошибка в тексте команды; команда не опознана
				//break;
				case '501':	//	 Синтаксическая ошибка в аргументах или параметрах команды
				//break;
				case '502':	//	 Данная команда не реализована
				//break;
				case '503':	//	 Неверная последовательность команд
				//break;
				case '504':	//	 У данной команды не может быть аргументов
				//break;
				case '550':	//	 Запрошенная команда не выполнена, так как почтовый ящик недоступен
				//break;
				case '551':	//	 Данный адресат не является местным; попробуйте передать сообщение по маршруту <forward-path>
				//break;
				case '552':	//	 Запрошенная команда почтовой транзакции прервана; дисковое пространство, доступное системе, переполнилось
				//break;
				case '553':	//	 Запрошенная команда не выполнена; указано недопустимое имя почтового ящика
				//break;
				case '554':	//	 Транзакция не выполнена
					dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, val));
				break;
			}
		}
		
		private function write(val:Array):void 
		{
			_socket.writeUTFBytes(val.join(NEW_LINE)+NEW_LINE);
			_socket.flush();
		}
	}

}